package com.ls.task.support.schedule.util;

import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.ScheduleBuilder;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;

import com.ls.commons.schedule.exception.MyScheduleException;
import com.ls.fw.commons.log.exception.ExceptionsHelper;
import com.ls.fw.commons.spring.SpringContextHolder;
import com.ls.task.support.schedule.bean.Task;
import com.ls.task.support.schedule.bean.TaskTrigger;

public class TaskUtils {

	private static final Logger logger = LoggerFactory.getLogger(TaskUtils.class);
	/**
	 * 校验Corn表达式
	 * 
	 * @param corn
	 * @return boolean
	 */
	public static boolean validateCornExpression(String corn) {
		return CronExpression.isValidExpression(corn);
	}

	public static JobDetail loadTask(Task sysTask){
		return loadTask((ConfigurableApplicationContext)SpringContextHolder.getApplicationContext(),sysTask);
	}
	public static JobDetail loadTask(ConfigurableApplicationContext configurableApplicationContext
			,Task sysTask){
		JobDetail jobDetail = null;
		String taskSpringId = sysTask.getSpringId()+""+sysTask.getId();  
		if(!SpringContextHolder.containsBean(taskSpringId)) {
			 // 获取bean工厂并转换为DefaultListableBeanFactory  
			 DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext  
					 .getBeanFactory();  
			 
			 // 通过BeanDefinitionBuilder创建bean定义  
			 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder  
					 .genericBeanDefinition(MethodInvokingJobDetailFactoryBean.class);  
			 check(sysTask.getSpringId(), sysTask.getJobClass());
			 beanDefinitionBuilder.addPropertyReference("targetObject", sysTask.getSpringId());
			 beanDefinitionBuilder.addPropertyValue("targetMethod", sysTask.getMethodName());
			 
			 // 注册bean  
			 defaultListableBeanFactory.registerBeanDefinition(taskSpringId,  
					 beanDefinitionBuilder.getRawBeanDefinition()); 
			 logger.info("spring 动态加载{}({})",taskSpringId,sysTask.getJobClass());
		 }
		 MethodInvokingJobDetailFactoryBean object = SpringContextHolder.getBean(taskSpringId);
		 jobDetail = object.getObject();
         JobDataMap newJobDataMap = new JobDataMap();
         newJobDataMap.put("taskId", sysTask.getId());
		 jobDetail.getJobDataMap().putAll(newJobDataMap);
		 return jobDetail;
	}

	public static void loanBean(ConfigurableApplicationContext configurableApplicationContext
			, Class clas, String springId){
		// 获取bean工厂并转换为DefaultListableBeanFactory  
		DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext  
		        .getBeanFactory();  
		  
		// 通过BeanDefinitionBuilder创建bean定义  
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder  
		        .genericBeanDefinition(clas);  
		  
		// 注册bean  
		defaultListableBeanFactory.registerBeanDefinition(springId,  
		        beanDefinitionBuilder.getRawBeanDefinition()); 
		logger.info("spring 动态加载{}({})",springId,clas.getName());
	}
	
	public static Object check(String springId, String target){
		Object object = null;
        if(SpringContextHolder.containsBean(springId)) {
            object = SpringContextHolder.getBean(springId);
        }else {
            try {
				object = SpringContextHolder.getBean(Class.forName(target));
			} catch (Exception e) {
				throw new IllegalArgumentException(ExceptionsHelper.getRootCause(e).getMessage(),e);
			}
        }
        if(object == null) {
            logger.warn(springId+"从Spring容器中无法获取目标执行类!");
            try {
				loanBean((ConfigurableApplicationContext)SpringContextHolder.getApplicationContext(), Class.forName(target), springId);
			} catch (ClassNotFoundException e) {
				throw new IllegalArgumentException(ExceptionsHelper.getRootCause(e).getMessage(),e);
			}
        }
        return object;
	}

    public static TriggerKey triggerKey(String triggerName, String triggerGroup) {
        return TriggerKey.triggerKey(triggerName, triggerGroup);
    }
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static Trigger trigger(Trigger oldt,TaskTrigger sysTaskTrigger,Task  sysTask) {
        TriggerBuilder tb = null;
        if(oldt == null) {
            tb = TriggerBuilder.newTrigger().withIdentity(triggerKey(sysTaskTrigger.getName()
            		,sysTaskTrigger.getGroup())).withSchedule(buildSchedule(
            				sysTaskTrigger.getCategoryCode(),sysTaskTrigger.getExpression()));
        }else {
            tb =  oldt.getTriggerBuilder()
            		.withIdentity(triggerKey(sysTask.getName()+sysTaskTrigger.getId()+sysTaskTrigger
            				.getName(),sysTask.getGroup()+sysTaskTrigger.getId()+sysTaskTrigger.getGroup()))
            		.withSchedule(buildSchedule(sysTaskTrigger.getCategoryCode(),sysTaskTrigger.getExpression()));
        }
        if(sysTask != null) {
            tb.forJob(jobKey(sysTask.getName(),sysTask.getGroup()));
        }
        if(sysTaskTrigger.getLevel()!=null) {
            tb.withPriority(sysTaskTrigger.getLevel());
        }
        if(sysTaskTrigger.getStartDt()!=null) {
            tb.startAt(sysTaskTrigger.getStartDt());
        }
        if(sysTaskTrigger.getEndDt()!=null) {
            tb.endAt(sysTaskTrigger.getEndDt());
        }
        JobDataMap newJobDataMap = new JobDataMap();
        newJobDataMap.put("triggerId", sysTaskTrigger.getId());
        newJobDataMap.put("taskId", sysTask.getId());
		tb.usingJobData(newJobDataMap );
        return tb.build();
    }

    public static JobKey jobKey(String jobName, String jobGroup) {
        return JobKey.jobKey(jobName, jobGroup);
    }
    @SuppressWarnings("rawtypes")
    public static ScheduleBuilder buildSchedule(String type,String expression) {
        ScheduleBuilder sb = null;
        if(type.equals("corn")) {
            if(!CronExpression.isValidExpression(expression)) {
                throw new MyScheduleException("ScheduleTrigger***配置信息***定时Corn表达式格式错误!!!");
            }
            sb = CronScheduleBuilder.cronSchedule(expression);
        }else {
            String subType = type.split("-")[1];
            String[] values = expression.trim().split("/");
            SimpleScheduleBuilder ssb = null;
            switch(subType) {
            case "S":
                ssb = SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(Integer.parseInt(values[0]));
                break;
            case "MI":
                ssb = SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInMinutes(Integer.parseInt(values[0]));
                break;
            case "H":
                ssb = SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInHours(Integer.parseInt(values[0]));
                break;
            case "D":
                sb = CronScheduleBuilder.dailyAtHourAndMinute(
                        Integer.parseInt(values[0]),
                        Integer.parseInt(values[1]));
                break;
            case "W":
                sb = CronScheduleBuilder.weeklyOnDayAndHourAndMinute(
                        Integer.parseInt(values[0]),
                        Integer.parseInt(values[1]),
                        Integer.parseInt(values[2]));
                break;
            case "M":
                sb = CronScheduleBuilder.monthlyOnDayAndHourAndMinute(
                        Integer.parseInt(values[0]),
                        Integer.parseInt(values[1]),
                        Integer.parseInt(values[2]));
                break;
            }
            if(ssb != null) {
                if(values[1].equalsIgnoreCase("N")) {
                    ssb.repeatForever();
                }else {
                    ssb.withRepeatCount(Integer.parseInt(values[1]));
                }
                sb = ssb;
            }
        }
        return sb;
    }
}
